package juc.scenarios.lock;

import java.util.Map;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * 读写锁演示
 * 使用ReentrantReadWriteLock实现读写分离的并发控制
 */
public class ReadWriteLockDemo {

    /**
     * 共享数据缓存
     */
    static class SharedDataCache {
        private final Map<String, String> data = new ConcurrentHashMap<>();
        private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
        private final ReentrantReadWriteLock.ReadLock readLock = rwLock.readLock();
        private final ReentrantReadWriteLock.WriteLock writeLock = rwLock.writeLock();

        /**
         * 读取数据（使用读锁）
         */
        public String getData(String key) {
            readLock.lock();
            try {
                // 模拟读取操作的耗时
                Thread.sleep(50);
                return data.get(key);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                return null;
            } finally {
                readLock.unlock();
            }
        }

        /**
         * 写入数据（使用写锁）
         */
        public void putData(String key, String value) {
            writeLock.lock();
            try {
                // 模拟写入操作的耗时
                Thread.sleep(100);
                data.put(key, value);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            } finally {
                writeLock.unlock();
            }
        }

        /**
         * 批量读取（使用读锁）
         */
        public Map<String, String> getAllData() {
            readLock.lock();
            try {
                // 模拟批量读取的耗时
                Thread.sleep(200);
                return new ConcurrentHashMap<>(data);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                return new ConcurrentHashMap<>();
            } finally {
                readLock.unlock();
            }
        }

        /**
         * 清空数据（使用写锁）
         */
        public void clear() {
            writeLock.lock();
            try {
                data.clear();
            } finally {
                writeLock.unlock();
            }
        }

        /**
         * 获取锁状态信息
         */
        public LockStatistics getStatistics() {
            return new LockStatistics(
                    rwLock.getReadLockCount(),
                    rwLock.getWriteHoldCount(),
                    rwLock.isWriteLocked(),
                    rwLock.getReadHoldCount()
            );
        }
    }

    /**
     * 锁统计信息
     */
    static class LockStatistics {
        private final int readLockCount;
        private final int writeHoldCount;
        private final boolean writeLocked;
        private final int readHoldCount;

        public LockStatistics(int readLockCount, int writeHoldCount, boolean writeLocked, int readHoldCount) {
            this.readLockCount = readLockCount;
            this.writeHoldCount = writeHoldCount;
            this.writeLocked = writeLocked;
            this.readHoldCount = readHoldCount;
        }

        @Override
        public String toString() {
            return String.format("ReadLocks: %d, WriteHold: %d, WriteLocked: %s, ReadHold: %d",
                    readLockCount, writeHoldCount, writeLocked, readHoldCount);
        }
    }

    /**
     * 读者任务
     */
    static class ReaderTask implements Runnable {
        private final SharedDataCache cache;
        private final String readerId;
        private final int readCount;
        private final Random random = new Random();

        public ReaderTask(SharedDataCache cache, String readerId, int readCount) {
            this.cache = cache;
            this.readerId = readerId;
            this.readCount = readCount;
        }

        @Override
        public void run() {
            System.out.println("[读者" + readerId + "] 开始读取数据...");

            for (int i = 0; i < readCount; i++) {
                String key = "key-" + random.nextInt(10);
                String value = cache.getData(key);

                System.out.println("[读者" + readerId + "] 读取 " + key + " = " + value);

                // 随机短暂停顿
                try {
                    Thread.sleep(10 + random.nextInt(50));
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    break;
                }
            }

            System.out.println("[读者" + readerId + "] 读取完成");
        }
    }

    /**
     * 写者任务
     */
    static class WriterTask implements Runnable {
        private final SharedDataCache cache;
        private final String writerId;
        private final int writeCount;
        private final Random random = new Random();

        public WriterTask(SharedDataCache cache, String writerId, int writeCount) {
            this.cache = cache;
            this.writerId = writerId;
            this.writeCount = writeCount;
        }

        @Override
        public void run() {
            System.out.println("[写者" + writerId + "] 开始写入数据...");

            for (int i = 0; i < writeCount; i++) {
                String key = "key-" + random.nextInt(10);
                String value = "value-" + writerId + "-" + i;

                cache.putData(key, value);
                System.out.println("[写者" + writerId + "] 写入 " + key + " = " + value);

                // 随机短暂停顿
                try {
                    Thread.sleep(20 + random.nextInt(100));
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    break;
                }
            }

            System.out.println("[写者" + writerId + "] 写入完成");
        }
    }

    /**
     * 性能测试任务
     */
    static class PerformanceTestTask implements Runnable {
        private final SharedDataCache cache;
        private final String taskId;
        private final boolean isReader;
        private final int operations;

        public PerformanceTestTask(SharedDataCache cache, String taskId, boolean isReader, int operations) {
            this.cache = cache;
            this.taskId = taskId;
            this.isReader = isReader;
            this.operations = operations;
        }

        @Override
        public void run() {
            long startTime = System.currentTimeMillis();
            Random random = new Random();

            for (int i = 0; i < operations; i++) {
                if (isReader) {
                    // 读操作
                    String key = "key-" + random.nextInt(100);
                    cache.getData(key);
                } else {
                    // 写操作
                    String key = "key-" + random.nextInt(100);
                    String value = "value-" + taskId + "-" + i;
                    cache.putData(key, value);
                }
            }

            long endTime = System.currentTimeMillis();
            String operationType = isReader ? "读" : "写";
            System.out.println("[性能测试] " + operationType + "任务" + taskId + " 完成" + operations +
                    "次操作，耗时: " + (endTime - startTime) + "ms");
        }
    }

    /**
     * 主方法：运行读写锁演示
     */
    public static void main(String[] args) throws InterruptedException {
        System.out.println("=== 读写锁演示开始 ===");
        System.out.println("演示时间: " + new java.util.Date());
        System.out.println();

        SharedDataCache cache = new SharedDataCache();

        // 1. 基本读写操作演示
        System.out.println("--- 基本读写操作演示 ---");
        demonstrateBasicReadWrite(cache);

        Thread.sleep(1000);

        // 2. 并发读写演示
        System.out.println("\n--- 并发读写演示 ---");
        demonstrateConcurrentReadWrite();

        Thread.sleep(1000);

        // 3. 性能对比测试
        System.out.println("\n--- 读写锁性能对比测试 ---");
        demonstratePerformanceComparison();

        System.out.println("\n=== 读写锁演示结束 ===");
    }

    /**
     * 基本读写操作演示
     */
    private static void demonstrateBasicReadWrite(SharedDataCache cache) {
        System.out.println("初始化数据...");
        cache.putData("key-1", "value-1");
        cache.putData("key-2", "value-2");

        System.out.println("读取数据:");
        System.out.println("key-1 = " + cache.getData("key-1"));
        System.out.println("key-2 = " + cache.getData("key-2"));
        System.out.println("不存在的key = " + cache.getData("key-3"));

        System.out.println("\n锁状态: " + cache.getStatistics());
    }

    /**
     * 并发读写演示
     */
    private static void demonstrateConcurrentReadWrite() throws InterruptedException {
        SharedDataCache cache = new SharedDataCache();

        // 初始化一些数据
        for (int i = 0; i < 5; i++) {
            cache.putData("key-" + i, "initial-value-" + i);
        }

        // 创建读者线程
        Thread[] readers = new Thread[3];
        for (int i = 0; i < readers.length; i++) {
            readers[i] = new Thread(new ReaderTask(cache, String.valueOf(i), 5));
        }

        // 创建写者线程
        Thread[] writers = new Thread[2];
        for (int i = 0; i < writers.length; i++) {
            writers[i] = new Thread(new WriterTask(cache, String.valueOf(i), 3));
        }

        // 启动所有线程
        System.out.println("启动读者线程...");
        for (Thread reader : readers) {
            reader.start();
        }

        Thread.sleep(200); // 让读者先运行一会儿

        System.out.println("启动写者线程...");
        for (Thread writer : writers) {
            writer.start();
        }

        // 等待所有线程完成
        for (Thread reader : readers) {
            reader.join();
        }
        for (Thread writer : writers) {
            writer.join();
        }

        System.out.println("\n最终数据状态:");
        Map<String, String> allData = cache.getAllData();
        allData.forEach((key, value) -> System.out.println(key + " = " + value));

        System.out.println("\n最终锁状态: " + cache.getStatistics());
    }

    /**
     * 性能对比测试
     */
    private static void demonstratePerformanceComparison() throws InterruptedException {
        SharedDataCache cache = new SharedDataCache();

        // 预热数据
        for (int i = 0; i < 100; i++) {
            cache.putData("key-" + i, "value-" + i);
        }

        int threadCount = 10;
        int operationsPerThread = 100;

        // 测试1: 读多写少场景 (90% 读，10% 写)
        System.out.println("\n--- 读多写少场景测试 (90% 读，10% 写) ---");
        performReadWriteTest(cache, threadCount, operationsPerThread, 0.9);

        Thread.sleep(1000);

        // 测试2: 读写均衡场景 (50% 读，50% 写)
        System.out.println("\n--- 读写均衡场景测试 (50% 读，50% 写) ---");
        performReadWriteTest(cache, threadCount, operationsPerThread, 0.5);

        Thread.sleep(1000);

        // 测试3: 写多读少场景 (10% 读，90% 写)
        System.out.println("\n--- 写多读少场景测试 (10% 读，90% 写) ---");
        performReadWriteTest(cache, threadCount, operationsPerThread, 0.1);
    }

    /**
     * 执行读写测试
     */
    private static void performReadWriteTest(SharedDataCache cache, int threadCount,
                                             int operationsPerThread, double readRatio)
            throws InterruptedException {

        Thread[] threads = new Thread[threadCount];
        int readerCount = (int) (threadCount * readRatio);
        int writerCount = threadCount - readerCount;

        System.out.println("线程数: " + threadCount + " (读者: " + readerCount + ", 写者: " + writerCount + ")");

        // 创建读者线程
        for (int i = 0; i < readerCount; i++) {
            threads[i] = new Thread(new PerformanceTestTask(cache, "R-" + i, true, operationsPerThread));
        }

        // 创建写者线程
        for (int i = readerCount; i < threadCount; i++) {
            threads[i] = new Thread(new PerformanceTestTask(cache, "W-" + (i - readerCount), false, operationsPerThread));
        }

        long startTime = System.currentTimeMillis();

        // 启动所有线程
        for (Thread thread : threads) {
            thread.start();
        }

        // 等待所有线程完成
        for (Thread thread : threads) {
            thread.join();
        }

        long endTime = System.currentTimeMillis();
        System.out.println("总耗时: " + (endTime - startTime) + "ms");
        System.out.println("吞吐量: " + (threadCount * operationsPerThread * 1000 / (endTime - startTime)) + " 操作/秒");
    }
}